﻿#include "device_check_form.h"
#include "callback/vchat/vchat_callback.h"

namespace nim_comp
{

const LPCTSTR DeviceCheckForm::kClassName = L"DeviceCheckForm";

DeviceCheckForm::DeviceCheckForm()
{

}

DeviceCheckForm::~DeviceCheckForm()
{

}

std::wstring DeviceCheckForm::GetSkinFolder()
{
	return L"device_check";
}

std::wstring DeviceCheckForm::GetSkinFile()
{
	return L"device_check.xml";
}

std::wstring DeviceCheckForm::GetWindowClassName() const
{
	return kClassName;
}

std::wstring DeviceCheckForm::GetWindowId() const
{
	return kClassName;
}

LRESULT DeviceCheckForm::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	VideoManager::GetInstance()->EndDevice(nim::kNIMDeviceTypeAudioIn, kDeviceSessionTypeSetting);
	VideoManager::GetInstance()->EndDevice(nim::kNIMDeviceTypeAudioOut, kDeviceSessionTypeSetting);
	VideoManager::GetInstance()->EndDevice(nim::kNIMDeviceTypeAudioOutChat, kDeviceSessionTypeSetting);
	VideoManager::GetInstance()->EndDevice(nim::kNIMDeviceTypeVideo, kDeviceSessionTypeSetting);

	return __super::OnClose(uMsg, wParam, lParam, bHandled);
}

void DeviceCheckForm::InitWindow()
{
	m_pRoot->AttachBubbledEvent(kEventAll, nbase::Bind(&DeviceCheckForm::Notify, this, std::placeholders::_1));
	m_pRoot->AttachBubbledEvent(kEventClick, nbase::Bind(&DeviceCheckForm::OnClicked, this, std::placeholders::_1));
	m_pRoot->AttachBubbledEvent(kEventSelect, nbase::Bind(&DeviceCheckForm::OnSelect, this, std::placeholders::_1));
	m_pRoot->AttachBubbledEvent(kEventUnSelect, nbase::Bind(&DeviceCheckForm::OnSelect, this, std::placeholders::_1));

	test_page_ = dynamic_cast<VBox*>(FindControl(L"test_page"));
	step_tabbox_ = dynamic_cast<TabBox*>(FindControl(L"step_tabbox"));
	notice_label_ = dynamic_cast<Label*>(FindControl(L"label_notice"));
	option_yes_ = dynamic_cast<Button*>(FindControl(L"btn_yes"));
	option_no_ = dynamic_cast<Button*>(FindControl(L"btn_no"));

	speaker_combo_ = dynamic_cast<Combo*>(FindControl(L"speaker_combo"));
	speaker_slider_ = dynamic_cast<Slider*>(FindControl(L"speaker_volumn"));
	speaker_progress_ = dynamic_cast<Progress*>(FindControl(L"speaker_progress"));
	speaker_normal_ = dynamic_cast<Control*>(FindControl(L"speaker_normal"));
	speaker_gif_ = dynamic_cast<Control*>(FindControl(L"speaker_gif"));
	speaker_test_ = dynamic_cast<Button*>(FindControl(L"speaker_test"));

	mic_combo_ = dynamic_cast<Combo*>(FindControl(L"mic_combo"));
	mic_slider_ = dynamic_cast<Slider*>(FindControl(L"mic_volumn"));
	mic_progress_ = dynamic_cast<Progress*>(FindControl(L"mic_progress"));
	mic_test_ = dynamic_cast<Button*>(FindControl(L"mic_test"));

	camera_combo_ = dynamic_cast<Combo*>(FindControl(L"camera_combo"));
	camera_bitmap_ = dynamic_cast<BitmapControl*>(FindControl(L"video_show"));
	camera_fail_ = dynamic_cast<Control*>(FindControl(L"camera_fail"));
	camera_error_notice_ = dynamic_cast<Label*>(FindControl(L"error_notice"));
	
	result_page_ = dynamic_cast<VBox*>(FindControl(L"result_page"));
	result_face_ = dynamic_cast<Control*>(FindControl(L"result_face"));
	result_title_ = dynamic_cast<Label*>(FindControl(L"result_title"));
	result_description_ = dynamic_cast<Label*>(FindControl(L"result_description"));
	result_list_ = dynamic_cast<ListBox*>(FindControl(L"result_list"));
	result_close_ = dynamic_cast<Button*>(FindControl(L"result_close"));

	InitDevices();

	nim::VChat::AddDeviceStatusCb(nim::kNIMDeviceTypeAudioIn, &VChatCallback::DeviceStatusCb);
	nim::VChat::AddDeviceStatusCb(nim::kNIMDeviceTypeVideo, &VChatCallback::DeviceStatusCb);
}

LRESULT DeviceCheckForm::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	return __super::HandleMessage(uMsg, wParam, lParam);
}

void DeviceCheckForm::OnFinalMessage(HWND hWnd)
{
	__super::OnFinalMessage(hWnd);
}

Control* DeviceCheckForm::CreateControl(const std::wstring& pstrClass)
{
	if (pstrClass == L"BitmapControl")
		return new BitmapControl(&nim_comp::VideoManager::GetInstance()->video_frame_mng_);

	return NULL;
}

bool DeviceCheckForm::Notify(EventArgs* msg)
{
	std::wstring name = msg->pSender->GetName();
	if (msg->Type == ui::kEventValueChange)
	{
		if (name == L"speaker_volumn")
		{
			int v = static_cast<int>(speaker_slider_->GetValue());

			VideoManager::GetInstance()->SetAudioVolumn(v, false);
		}
		else if (name == L"mic_volumn")
		{
			int v = static_cast<int>(mic_slider_->GetValue());

			VideoManager::GetInstance()->SetAudioVolumn(v, true);
			VideoManager::GetInstance()->SetAudioInputAutoVolumn(false);
		}
	}

	return true;
}

bool DeviceCheckForm::OnClicked(EventArgs* msg)
{
	std::wstring name = msg->pSender->GetName();
	if (name == L"btn_yes")
	{
		SwitchPage(true);
	}
	else if (name == L"btn_no")
	{
		SwitchPage(false);
	}
	else if (name == L"result_close")
	{
		Close();
	}
	else if (name == L"speaker_test")
	{
		TestSpeaker();
	}
	else if (name == L"mic_test")
	{
		TestMicrophone();
	}

	return true;
}

bool DeviceCheckForm::OnSelect(EventArgs* arg)
{
	std::wstring name = arg->pSender->GetName();
	if (name == L"camera_combo")
	{
		int k = camera_combo_->GetCurSel();
		ListContainerElement* label = (ListContainerElement*)camera_combo_->GetItemAt(k);
		if (label)
		{
			test_camera_name_ = label->GetUTF8Text();
			VideoManager::GetInstance()->StartDevice(nim::kNIMDeviceTypeVideo, label->GetUTF8DataID(), kDeviceSessionTypeSetting);
		}
	}
	return true;
}

void DeviceCheckForm::InitDevices()
{
	// 枚举扬声器、麦克风和摄像头设备列表
	auto InsertDevices = [](const std::list<MEDIA_DEVICE_DRIVE_INFO>& devices, Combo* combo) {
		for (auto device : devices)
		{
			ListContainerElement* label = new ListContainerElement;
			label->SetClass(L"listitem");
			label->SetFixedHeight(30);
			label->SetTextPadding(UiRect(10, 1, 30, 1));
			label->SetUTF8Text(device.friendly_name_);
			label->SetUTF8DataID(device.device_path_);
			combo->Add(label);
		}

		if (devices.size() > 0) combo->SelectItem(0);
	};

	speaker_combo_->RemoveAll();
	mic_combo_->RemoveAll();
	camera_combo_->RemoveAll();

	auto speakers = VideoManager::GetInstance()->GetDeviceInfo(nim::kNIMDeviceTypeAudioOut);
	InsertDevices(speakers, speaker_combo_);

	auto microphone = VideoManager::GetInstance()->GetDeviceInfo(nim::kNIMDeviceTypeAudioIn);
	InsertDevices(microphone, mic_combo_);

	auto cameras = VideoManager::GetInstance()->GetDeviceInfo(nim::kNIMDeviceTypeVideo);
	InsertDevices(cameras, camera_combo_);

	speaker_slider_->SetValue(255);
	mic_slider_->SetValue(255);

	std::string file_path = nbase::UTF16ToUTF8(QPath::GetAppPath() + L"rain.wav");
	nim::VChat::PreloadEffect(1, file_path, [](int id, bool ret, int code, const std::string& json_extension) {
		QLOG_APP(L"prelaod effect id = {0}, ret = {1}, code = {2}") << id << ret << code;
	});
}

void DeviceCheckForm::SwitchPage(bool test_success/* = true*/)
{
	// 翻页，为下一页准备数据
	auto current_page = step_tabbox_->GetCurSel();
	ui::MutiLanSupport *multilan = ui::MutiLanSupport::GetInstance();
	DEVICE_STATUS device_status;

	if (current_page == kSpeakerTestPage)
	{
		step_tabbox_->SelectItem(current_page + 1);
		nim::VChat::StopEffect(1, nullptr);
		FindControl(L"step_circle_" + std::to_wstring(current_page + 1))->SetClass(L"step_pass");

		device_status.device_name = test_speaker_name_;
		device_status.is_success = test_success;
		result_[nim::kNIMDeviceTypeAudioOutChat] = device_status;

		option_yes_->SetEnabled(false);
		option_no_->SetEnabled(false);

		notice_label_->SetText(test_success ? multilan->GetStringViaID(L"STRID_DEVICE_CHECK_MIC_NOTICE")
			: multilan->GetStringViaID(L"STRID_DEVICE_CHECK_MIC_NOTICE2"));
	}
	else if (current_page == kMicrophoneTestPage)
	{
		step_tabbox_->SelectItem(current_page + 1);
		int k = camera_combo_->GetCurSel();
		ListContainerElement* label = (ListContainerElement*)camera_combo_->GetItemAt(k);
		if (label)
		{
			test_camera_name_ = label->GetUTF8Text();
			VideoManager::GetInstance()->StartDevice(nim::kNIMDeviceTypeVideo, label->GetUTF8DataID(), kDeviceSessionTypeSetting);
		}
		FindControl(L"step_circle_" + std::to_wstring(current_page + 1))->SetClass(L"step_pass");

		device_status.device_name = test_mic_name_;
		device_status.is_success = test_success;
		result_[nim::kNIMDeviceTypeAudioIn] = device_status;

		notice_label_->SetText(multilan->GetStringViaID(L"STRID_DEVICE_CHECK_CAMERA_NOTICE"));
	}
	else if (current_page == kCameraTestPage)
	{
		test_page_->SetVisible(false);
		result_page_->SetVisible(true);
		VideoManager::GetInstance()->EndDevice(nim::kNIMDeviceTypeAudioIn, kDeviceSessionTypeSetting);
		VideoManager::GetInstance()->EndDevice(nim::kNIMDeviceTypeAudioOut, kDeviceSessionTypeSetting);
		VideoManager::GetInstance()->EndDevice(nim::kNIMDeviceTypeVideo, kDeviceSessionTypeSetting);

		device_status.device_name = test_camera_name_;
		device_status.is_success = test_success;
		result_[nim::kNIMDeviceTypeVideo] = device_status;
		UpdateResultList();
	}
}

void DeviceCheckForm::PaintVideo()
{
	int cur = step_tabbox_->GetCurSel();
	if (cur == kCameraTestPage && camera_is_open_)
	{
		camera_bitmap_->Refresh(this);
	}
}

void DeviceCheckForm::OnVideoDeviceStartCallback(bool result)
{
	camera_is_open_ = result;
	if (result)
	{
		paint_video_timer_.Cancel();
		StdClosure task = nbase::Bind(&DeviceCheckForm::PaintVideo, this);
		nbase::ThreadManager::PostRepeatedTask(kThreadUI, paint_video_timer_.ToWeakCallback(task), nbase::TimeDelta::FromMilliseconds(70));
		camera_fail_->SetVisible(false);
		camera_error_notice_->SetVisible(false);
	}
	else
	{
		camera_fail_->SetVisible(true);
		camera_error_notice_->SetVisible(true);
	}
}

void DeviceCheckForm::OnDeviceChange(uint32_t status)
{
	if (status & nim::kNIMDeviceStatusWorkRemove || status & nim::kNIMDeviceStatusChange)
	{
		InitDevices();
		speaker_test_->SetEnabled(true);
		mic_test_->SetEnabled(true);

		if (step_tabbox_->GetCurSel() == kSpeakerTestPage)
		{
			VideoManager::GetInstance()->EndDevice(nim::kNIMDeviceTypeAudioOut, kDeviceSessionTypeSetting);
			nim::VChat::StopEffect(1, nullptr);
			option_yes_->SetEnabled(false);
			option_no_->SetEnabled(false);
		}
		else if (step_tabbox_->GetCurSel() == kMicrophoneTestPage)
		{
			VideoManager::GetInstance()->EndDevice(nim::kNIMDeviceTypeAudioIn, kDeviceSessionTypeSetting);
			option_yes_->SetEnabled(false);
			option_no_->SetEnabled(false);
		}
		else if (step_tabbox_->GetCurSel() == kCameraTestPage)
		{
			VideoManager::GetInstance()->EndDevice(nim::kNIMDeviceTypeVideo, kDeviceSessionTypeSetting);
			int k = camera_combo_->GetCurSel();
			ListContainerElement* label = (ListContainerElement*)camera_combo_->GetItemAt(k);
			if (label)
			{
				test_camera_name_ = label->GetUTF8Text();
				VideoManager::GetInstance()->StartDevice(nim::kNIMDeviceTypeVideo, label->GetUTF8DataID(), kDeviceSessionTypeSetting);
			}
		}
	}
}

void DeviceCheckForm::TestSpeaker()
{
	// 开始测试前打开扬声器设备
	int k = speaker_combo_->GetCurSel();
	ListContainerElement* label = (ListContainerElement*)speaker_combo_->GetItemAt(k);
	if (label)
	{
		test_speaker_name_ = label->GetUTF8Text();
		// kNIMDeviceTypeAudioOutChat 为播放音效使用，kNIMDeviceTypeAudioOut 为麦克风测试使用
		VideoManager::GetInstance()->StartDevice(nim::kNIMDeviceTypeAudioOutChat, label->GetUTF8DataID(), kDeviceSessionTypeSetting);
		VideoManager::GetInstance()->StartDevice(nim::kNIMDeviceTypeAudioOut, label->GetUTF8DataID(), kDeviceSessionTypeSetting);
	}

	// 播放音效文件
	speaker_test_->SetEnabled(false);
	speaker_normal_->SetVisible(false);
	speaker_gif_->SetVisible(true);
	nim::VChat::EffectOptCallback cb = nbase::Bind(&DeviceCheckForm::PlayEffectCallback, this, speaker_test_, \
		std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
	nim::VChat::PlayEffect(1, 1, cb);

	nbase::ThreadManager::PostDelayedTask(kThreadUI, ToWeakCallback([this](){
		option_yes_->SetEnabled(true);
		option_no_->SetEnabled(true);
	}), nbase::TimeDelta::FromSeconds(5));
}

void DeviceCheckForm::TestMicrophone()
{
	// 开始测试前打开扬声器设备
	int k = mic_combo_->GetCurSel();
	ListContainerElement* label = (ListContainerElement*)mic_combo_->GetItemAt(k);
	if (label)
	{
		test_mic_name_ = label->GetUTF8Text();
		VideoManager::GetInstance()->StartDevice(nim::kNIMDeviceTypeAudioIn, label->GetUTF8DataID(), kDeviceSessionTypeSetting);
	}

	mic_test_->SetEnabled(false);
	nbase::ThreadManager::PostDelayedTask(kThreadUI, ToWeakCallback([this](){
		option_yes_->SetEnabled(true);
		option_no_->SetEnabled(true);
		mic_test_->SetEnabled(true);
	}), nbase::TimeDelta::FromSeconds(5));
}

void DeviceCheckForm::OnPlayAudioVolumnCb(bool is_capture, int volumn)
{
	if (is_capture)
	{
		mic_progress_->SetValue(volumn);
	}
	else
	{
		speaker_progress_->SetValue(volumn);
	}
}

void DeviceCheckForm::PlayEffectCallback(ui::Control* pSender, int id, bool ret, int code, const std::string& json_extension)
{
	QLOG_APP(L"id = {0}, ret = {1}, code = {2}") << id << ret << code;
	pSender->SetEnabled();
	speaker_normal_->SetVisible(true);
	speaker_gif_->SetVisible(false);
}

void DeviceCheckForm::UpdateResultList()
{
	bool has_error = false;

	for (auto iter = result_.begin(); iter != result_.end(); iter++)
	{
		ListContainerElement* element = new ListContainerElement;
		GlobalManager::FillBoxWithCache(element, L"device_check/result_item.xml");
		auto device = element->FindSubControl(L"device");
		auto device_name = dynamic_cast<Label*>(element->FindSubControl(L"device_name"));
		auto status = element->FindSubControl(L"status");

		switch (iter->first)
		{
		case nim::kNIMDeviceTypeAudioOutChat:
			device->SetBkImage(L"speaker_icon.png");
			break;
		case nim::kNIMDeviceTypeAudioIn:
			device->SetBkImage(L"mic_icon.png");
			break;
		case nim::kNIMDeviceTypeVideo:
			device->SetBkImage(L"camera_icon.png");
			break;
		}

		device_name->SetUTF8Text(iter->second.device_name);
		status->SetBkImage(iter->second.is_success ? L"success_icon.png" : L"fail_icon.png");

		if (!iter->second.is_success)
			has_error = true;

		result_list_->Add(element);
	}

	ui::MutiLanSupport *multilan = ui::MutiLanSupport::GetInstance();
	result_face_->SetBkImage(has_error ? L"cry.png" : L"smile.png");
	result_title_->SetText(has_error ? multilan->GetStringViaID(L"STRID_DEVICE_CHECK_RESULT_TITLE_WITH_ERROR") : multilan->GetStringViaID(L"STRID_DEVICE_CHECK_RESULT_TITLE"));
	result_description_->SetText(has_error ? multilan->GetStringViaID(L"STRID_DEVICE_CHECK_RESULT_HAS_ERROR") : multilan->GetStringViaID(L"STRID_DEVICE_CHECK_RESULT_NO_ERROR"));
}

}
